#include "StdAfx.h"
#include "XDropListenerImpl.h"


IMPLEMENT_SKIN_CLASS(XDropListenerImpl);

//////////////////////////////////////////////////////////////////////////

XDropTargetImpl::XDropTargetImpl(XDropListenerImpl* pListener)
{
    m_pListener = pListener;
}

XDropTargetImpl::~XDropTargetImpl()
{
    m_pListener = NULL;
}

HRESULT XDropTargetImpl::internalQueryInterface(REFIID riid, void **ppvObject)
{
    if(IsEqualIID(riid, __uuidof(IUnknown)))
    {
        *ppvObject = dynamic_cast<IUnknown*>(this);
        if((*ppvObject) != NULL)
        {
            AddRef();
            return S_OK;
        }
    }
    return superclass::internalQueryInterface(riid, ppvObject);
}

HRESULT XDropTargetImpl::DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
    return m_pListener->OnDragEnter(pDataObj, grfKeyState, pt, pdwEffect);
}

HRESULT XDropTargetImpl::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
    return m_pListener->OnDragOver(grfKeyState, pt, pdwEffect);
}

HRESULT XDropTargetImpl::DragLeave(void)
{
    return m_pListener->OnDragLeave();
}

HRESULT XDropTargetImpl::Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
    return m_pListener->OnDrop(pDataObj, grfKeyState, pt, pdwEffect);
}


//////////////////////////////////////////////////////////////////////////
XDropListenerImpl::XDropListenerImpl(void)
{
    m_hWnd = NULL;
    m_pDropCallback = NULL;
    m_pDropTarget = XAllocD(XDropTargetImpl, this);
}

XDropListenerImpl::~XDropListenerImpl(void)
{
}

//////////////////////////////////////////////////////////////////////////
// XDropListenerImpl

HRESULT XDropListenerImpl::Enable(XWND xWnd, BOOL bEnable)
{
    if(bEnable)
    {
        m_hWnd = xWnd;
        return ::RegisterDragDrop((HWND)m_hWnd, m_pDropTarget);
    }
    else
    {
        m_hWnd = NULL;
        return ::RevokeDragDrop((HWND)m_hWnd);
    }
}

HRESULT XDropListenerImpl::RegisterCallback(IXDropCallback* pCallback)
{
    m_pDropCallback = pCallback;
    return S_OK;
}

HRESULT XDropListenerImpl::UnRegisterCallback()
{
    m_pDropCallback = NULL;
    return S_OK;
}

//////////////////////////////////////////////////////////////////////////
// IDropListener
HRESULT XDropListenerImpl::OnDragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
    if(m_pDropCallback == NULL)
        return S_OK;

    FORMATETC formatEtc = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL|TYMED_MFPICT};

    STGMEDIUM stgMedium;
    SCODE sc = pDataObj->GetData(&formatEtc, &stgMedium);
    if (FAILED(sc))
        return E_INVALIDARG;

    UINT uNumFiles = 0;
    *pdwEffect = XDE_None;
    do 
    {
        if(stgMedium.hGlobal == NULL)
            return E_INVALIDARG;

        HGLOBAL hGlobal = stgMedium.hGlobal;

        if(NULL != hGlobal)
        {
            HDROP hDrop = (HDROP)GlobalLock(hGlobal);
            uNumFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
            if(uNumFiles > 0 && uNumFiles != -1)
            {
                *pdwEffect = (DWORD)QueryCallbackEffect(pt);
            }

            GlobalUnlock (hGlobal);
        }
    } while (FALSE);

    ::ReleaseStgMedium(&stgMedium);
    return (uNumFiles != 0) ? S_OK : E_INVALIDARG;
}

HRESULT XDropListenerImpl::OnDragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
    *pdwEffect = (DWORD)QueryCallbackEffect(pt);
    return S_OK;
}

HRESULT XDropListenerImpl::OnDragLeave(void)
{
    return S_OK;
}

HRESULT XDropListenerImpl::OnDrop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
    if(m_pDropCallback == NULL)
        return S_OK;

    FORMATETC formatEtc = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL|TYMED_MFPICT};

    STGMEDIUM stgMedium;
    SCODE sc = pDataObj->GetData(&formatEtc, &stgMedium);
    if (FAILED(sc))
        return E_INVALIDARG;

    do 
    {
        if(stgMedium.hGlobal == NULL)
            return E_INVALIDARG;

        HGLOBAL hGlobal = stgMedium.hGlobal;

        if(NULL != hGlobal)
        {
            HDROP hDrop = (HDROP)GlobalLock(hGlobal);

            if(hDrop != NULL)
            {
                UINT  uNumFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
                m_pDropCallback->OnFileDropped(uNumFiles, hDrop);
                DragFinish(hDrop);
            }

            GlobalUnlock (hGlobal);
        }
    } while (FALSE);

    return S_OK;
}

//////////////////////////////////////////////////////////////////////////
// IXBaseObj
void XDropListenerImpl::OnFinalRelease()
{
    superclass::OnFinalRelease();
    XSAFE_RELEASE(m_pDropTarget);
}

XDropEffect XDropListenerImpl::QueryCallbackEffect(const POINTL& pt)
{
    if(m_pDropCallback != NULL)
    {
        POINT Pt = {pt.x, pt.y};
        ::ScreenToClient((HWND)m_hWnd, &Pt);
        return m_pDropCallback->QueryEffect(&Pt);
    }
    return XDE_None;
}
